<?php

namespace JsonLowCode;

use Closure;
use JsonLowCode\Support\Fluent;
use JsonLowCode\UI\AppAction;
use JsonLowCode\UI\AppForm;
use JsonLowCode\Form\Grid;
use JsonLowCode\Form\Group;
use JsonLowCode\Form\Tabs;
use JsonLowCode\Factory\FormFactory;
use JsonLowCode\Helps\Random;
use JsonLowCode\Helps\Tools;
use JsonLowCode\Support\Js;

/**
 * @method static Form make($repository = null, ?Closure $builder = null, $request = null) Form
 * @method AppForm size(string $size = 'small' | 'medium' | 'large',  bool $isJSExpression = false) AppForm
 */
class Form extends FormFactory
{

    /**
     * @var array<Schema>
     */
    protected array $elements = [];

    /**
     * @var array<string,mixed>
     */
    protected array $fields = [];


    /**
     * @var array<string,mixed>
     */
    protected array $data = [];

    /**
     * 表单标题
     * @var string
     */
    protected string $title = "";

    /**
     * @var Fluent
     */
    protected Fluent $eloquent;

    protected string $paramKey = "id";

    private $request;

    protected array $tabs;

    protected UI\Button|null $submitButton = null;

    protected string $apiUrl = "";

    protected ?Axios $axios = null;

    protected string $tableName = "";

    protected string $beforeScript = "";

    protected string $thenScript = "";

    protected string $catchScript = "";

    protected string $eventName = "";

    protected ?Closure $callback = null;

    /**
     * @var
     */
    protected  $repository = null;

    /**
     * 主键值
     * @var mixed|null
     */
    protected mixed $resourceId = null;

    /**
     * @param string|null $repository
     * @param Closure|null $callback
     * @param null $request
     */
    public function __construct(string $repository = null, Closure $callback = null, $request = null)
    {
        parent::__construct();
        $this->form = new AppForm();
        $this->element = $this->form;
        $this->request = $request;
        $this->callback = $callback;
        $this->repository = $repository;
        $this->setBindKey("form_".Random::alpha(8));
        $this->eloquent(new Fluent());
        $this->eventName = strToUcWords($this->bindKey." submit");
        $this->render();
    }

    public function eloquent($model = null)
    {
        if ($model === null) {
            return $this->eloquent;
        }
        if (is_array($model)) {
            $model = new Fluent($model);
        }
        $this->eloquent = $model;
        return $this->eloquent;
    }

    public function repository()
    {
        return $this->repository;
    }

    /**
     * Get primary key name of model.
     *
     * @return string
     */
    public function keyName(): string
    {
        if (!$this->repository) {
            return 'id';
        }
        return $this->repository->getKeyName();
    }

    /**
     * @return $this
     */
    public function edit($id): static
    {
        $this->resourceId = $id;
        $this->eloquent($this->repository->edit($this));
        return $this;
    }

    /**
     * @return array
     */
    public function getData(): array
    {
        return $this->data;
    }

    public function getKey(): mixed
    {
        return $this->resourceId;
    }

    /**
     * 设置组件model 双向绑定数据源
     * @param mixed $value
     * @return AppForm
     */
    public function formModel(string $value): AppForm
    {
        return $this->form->bindModel('model', $value);
    }

    /**
     * 设置接收参数
     * @param string $key
     * @return $this
     */
    public function setParamKey(string $key = "id"): static
    {
        $this->paramKey = $key;
        return $this;
    }

    /**
     * 获取当前绑定的变量
     * @return string
     */
    public function getBindKey(): string
    {
        return $this->bindKey;
    }

    /**
     * AppForm 组件
     * @param Closure|null $closure
     * @return AppForm
     */
    public function form(Closure $closure = null): AppForm
    {
        !is_null($closure)&&call_user_func($closure,$this->form);
        return $this->form;
    }

    /**
     * 获取当前表名
     * @return string
     */
    public function getTableName(): string
    {
        return $this->tableName;
    }

    /**
     * 设置接收参数
     * @param string|UI\Button $text
     * @param callable|null $callback
     * @return UI\Button
     */
    public function setSubmitButton(string|UI\Button $text = "", callable $callback = null): UI\Button
    {
        if ($text instanceof UI\Button){
            $this->submitButton = $text;
        }else{
            $this->submitButton = new UI\Button($text);
        }
        if ($callback!=null&&is_callable($callback)){
            $callback($this->submitButton);
        }
        return $this->submitButton;
    }

    /**
     * 选项卡
     * @param Closure $closure
     * @return Tabs
     */
    public function tabs(Closure $closure): Tabs
    {
        $tabs = new Tabs($this, $closure);
        return tap($tabs,function ($tabs) {
            $this->elements[] = $tabs;
        });
    }

    /**
     * @param Closure $closure
     * @param int|string|null $cols
     * @return Grid
     */
    public function grid(Closure $closure , int|string $cols = null): Grid
    {
        $grid = new Grid($this, $closure);
        $cols!=null&&$grid->cols($cols);
        return tap($grid,function ($grid) {
            $this->elements->push($grid);
        });
    }


    /**
     * 组合表单
     * @param Closure|null $closure
     * @param string|null $groupName
     * @return Group
     */
    public function group(Closure $closure = null, string $groupName = null): Group
    {
        if ($groupName){
            $this->divider($groupName);
        }
        $group = new Group($this, $closure);
        return tap($group,function ($group) {
            $this->elements->push($group);
        });
    }

    public function setFieldValue(string $field, mixed $value = null): static
    {
        $this->fields->put($field, $value);
        return $this;
    }

    public function getFields(): array
    {
        return $this->fields;
    }


    private function getFieldParams(): array
    {
//        return array_map(function ($element){
//           return $element->getFieldParams();
//        },$this->elements)->collapse()->all();
    }

    /**
     * 设置前端请求前事件
     * @param string $beforeScript
     * @return $this
     */
    public function beforeScript(string $beforeScript = "()=>{}"): static
    {
        $this->beforeScript = $beforeScript;
        return $this;
    }

    /**
     * 设置前端请求后then事件
     * @param string $thenScript
     * @return $this
     */
    public function thenScript(string $thenScript = "(result)=>{}"): static
    {
        $this->thenScript = $thenScript;
        return $this;
    }

    /**
     * 设置前端请求后catch事件
     * @param string $catchScript
     * @return $this
     */
    public function catchScript(string $catchScript = "(error)=>{}"): static
    {
        $this->catchScript = $catchScript;
        return $this;
    }

    /**
     * 设置前端请求后catch事件
     * @return $this
     */
    public function onBeforeSubmit($script = "function(state,callback){return callback(bool)}"): static
    {
        $this->form->events("onBeforeSubmit",$script);
        return $this;
    }

    /**
     * 获取模型
     */
    public function onSubmit($script = "function(){}")
    {
        $this->form->events("onSubmit",$script);
//        if ($this->axios){
//            $this->form->setPageDataSource($this->eventName,$this->axios);
//        }
//        $method = ["async (ev)=>{", "    ev.preventDefault();"];
//        if ($this->beforeScript){
//            $method[] = $this->beforeScript;
//        }
//        $mainScript =  "    this.dataSourceMap.{$this->eventName}.load()";
//        if ($this->thenScript){
//            $mainScript .= ".then({$this->thenScript})";
//        }
//        if ($this->catchScript){
//            $mainScript .= ".catch({$this->catchScript})";
//        }
//        $method[] = $mainScript;
//        $method[] = "}";
//        if (!$this->submitButton){
//            $this->setSubmitButton("submit");
//        }
//        $this->submitButton->attrType("submit");
//        $this->submitButton->onClick(implode("\n",$method));
//        return $this->formItem($this->submitButton);
        return $this;
    }


    /**
     * @param Closure|null $callback
     * @return $this
     */
    public function callback(Closure $callback = null): static
    {
        $this->callback = $callback;
        return $this;
    }

    /**
     * 弹窗表单打开按钮
     * @param string $text
     * @param string $modalTitle
     * @param Closure|null $callback
     * @return AppAction
     */
    public function modalButton(string $text = "添加表单",string $modalTitle = "", Closure $callback = null): AppAction
    {
        $variable = "showModal".Random::alpha(8);
        $action = new UI\AppAction($text);
        $action->primary()->slotIcon(UI::svgIcon("fa:plus"));
        $action->eventType('modal')->eventKey($variable)->eventValue(true);
        $modal = UI::modal($modalTitle?:$text);
        $modal->width(600)->bind('modal-key',$variable);
        if ($callback instanceof Closure){
            call_user_func($callback, $modal);
        }
        $this->form->type('modal');
        $this->form->modalKey($variable);
        $this->form->modalProps($modal->getProps());
        $action->children($this);
        return $action;
    }

    /**
     * 抽屉表单打开按钮
     * @param string $text 按钮标题
     * @param string $drawerTitle 抽屉标题
     * @param Closure|null $callback
     * @return AppAction
     */
    public function drawerButton(string $text = "添加表单", string $drawerTitle = "",Closure $callback = null): UI\AppAction
    {
        $variable = "showDrawer".Random::alpha(8);
        $action = new UI\AppAction($text);
        $action->primary()->slotIcon(UI::svgIcon("fa:plus"));
        $action->eventType('drawer')->eventKey($variable)->eventValue(true);
        $drawer = UI::drawer($drawerTitle?:$text);
        $drawer->width(600)->bind('drawer-key',$variable);
        if ($callback instanceof Closure){
            call_user_func($callback,$drawer);
        }
        $this->form->type('drawer');
        $this->form->drawerKey($variable);
        $this->form->drawerProps($drawer->getProps());
        $action->children($this->jsonSerialize());
        return $action;
    }


    /**
     * @return array
     */
    public function elements(): array
    {
        return $this->elements;
    }


    public function buildField(): void
    {
        if (!$this->elements){
            $this->form->children($this->elements);
        }
        if (!$this->rules){
            $this->form->prop("rules",$this->rules);
        }
        $this->buildFormModel();
    }

    /**
     * @param array $data
     * @return array
     */
    public function setData(array $data = []):array
    {
        if (!$data){
            foreach ($this->fields as $field=>$value){
                $fields = null;
                if (str_contains($field,".")){
                    $fields = explode(".",$field);
                }elseif (str_contains($field,"->")){
                    $fields = explode("->",$field);
                }
                if ($fields){
                    $merges = array_reduce(array_reverse($fields),function ($next, $prev) use ($value) {

                        return [$prev=>$next!=null?$next:$value];
                    });
                }else{
                    $merges[$field] = $value;
                }
                foreach ($merges as $key=>$merge){
                    $this->data[$key] = $merge;
                }
            }
        }else{
            $this->data =  $data;
        }
        return $this->getData();
    }

    public function buildFormModel(): void
    {
        $this->setData();
        if ($this->mode=="form"){
            $this->formModel(Js::this($this->bindKey));
            $this->form->setState($this->bindKey, $this->getData());
        }
    }

    protected function render(): void
    {
        if ($this->callback instanceof Closure){
            call_user_func($this->callback,$this);
        }
        $this->buildField();
    }

    public function jsonSerialize(): array
    {
        return $this->form->jsonSerialize();
    }
}
